#include <GameEngine/UnitTest/unit_test.h>

#include <GameEngine/Core/Factory/factory_builder.h>
#include <GameEngine/Core/Factory/factory_manager.h>
#include <GameEngine/Core/Classes/rtti.h>


#ifdef GAMEENGINE_NAMESPACES
namespace GameEngine {
#endif


#ifndef DOXYGEN_IGNORE

//! Test class A
class _UnitTest_decl Factory_A {
	GAMEENGINE_DECLARE_RTTI_SINGLE(Factory_A)

public:
	Factory_A() {}
	virtual ~Factory_A() {}
};

//! Test class B (derives from test class A)
class _UnitTest_decl Factory_B : public Factory_A {
	GAMEENGINE_DECLARE_RTTI_PARENT(Factory_B,Factory_A)

public:
	Factory_B() {}
	virtual ~Factory_B() {}
};

//! Test class C (derives from test class A)
class _UnitTest_decl Factory_C : public Factory_A {
	GAMEENGINE_DECLARE_RTTI_PARENT(Factory_C,Factory_A)

public:
	Factory_C() {}
	virtual ~Factory_C() {}
};

//! Test class D (does NOT derive from test class A)
class _UnitTest_decl Factory_D {
	GAMEENGINE_DECLARE_RTTI_SINGLE(Factory_D)

public:
	Factory_D() {}
	virtual ~Factory_D() {}
};

//! Builder for test class B
class _UnitTest_decl Factory_B_Builder : public FactoryBuilder {

public:
	Factory_B_Builder() {}
	virtual ~Factory_B_Builder() {}

	virtual void* create_object() { return MemoryManager::instance()->allocate<Factory_B>(); }
};

//! Builder for test class C
class _UnitTest_decl Factory_C_Builder : public FactoryBuilder {

public:
	Factory_C_Builder() {}
	virtual ~Factory_C_Builder() {}

	virtual void* create_object() { return MemoryManager::instance()->allocate<Factory_C>(); }
};

//! Builder for test class D
class _UnitTest_decl Factory_D_Builder : public FactoryBuilder {

public:
	Factory_D_Builder() {}
	virtual ~Factory_D_Builder() {}

	virtual void* create_object() { return MemoryManager::instance()->allocate<Factory_D>(); }
};

GAMEENGINE_IMPLEMENT_RTTI(Factory_A)
GAMEENGINE_IMPLEMENT_RTTI(Factory_B)
GAMEENGINE_IMPLEMENT_RTTI(Factory_C)
GAMEENGINE_IMPLEMENT_RTTI(Factory_D)

GAMEENGINE_IMPLEMENT_SINGLETON( FactoryManager<Factory_A> )

#endif


//! Launch the unit tests on factories
/*!
 @return True if all unit tests on factories were
         succesful, false otherwise
 */
bool UnitTest::test_core_factory_functions() {
	bool res = true;

	// Get number of bytes
	MemoryManager::BytesCount bytes = MemoryManager::instance()->bytes();

	// Test factory bindings
	FactoryManager<Factory_A>* manager = FactoryManager<Factory_A>::instance();
	{{
		Factory_B_Builder* b_builder = MemoryManager::instance()->allocate<Factory_B_Builder>();
		res &= UNIT_TEST_ERROR( "[Factory] Test bind()", ( manager->bind<Factory_B>(b_builder) == true ));
		Factory_C_Builder* c_builder = MemoryManager::instance()->allocate<Factory_C_Builder>();
		res &= UNIT_TEST_ERROR( "[Factory] Test bind()", ( manager->bind<Factory_C>(c_builder) == true ));
		Factory_D_Builder* d_builder = MemoryManager::instance()->allocate<Factory_D_Builder>();
		res &= UNIT_TEST_ERROR( "[Factory] Test bind()", ( manager->bind<Factory_D>(d_builder) == false ));
		MemoryManager::instance()->desallocate(d_builder);
	}}

	// Test object creation
	{{
		Factory_A* obj_b = manager->create_object<Factory_B>();
		res &= UNIT_TEST_ERROR( "[Factory] Test create_object()", ( obj_b != nil ));
		Factory_A* obj_c = manager->create_object<Factory_C>();
		res &= UNIT_TEST_ERROR( "[Factory] Test create_object()", ( obj_c != nil ));
		Factory_A* obj_d = manager->create_object<Factory_D>();
		res &= UNIT_TEST_ERROR( "[Factory] Test create_object()", ( obj_d == nil ));
		MemoryManager::instance()->desallocate(obj_b);
		MemoryManager::instance()->desallocate(obj_c);
	}}

	// Test unbind
	{{
		res &= UNIT_TEST_ERROR( "[Factory] Test unbind()", ( manager->unbind<Factory_B>() == true ));
		Factory_A* obj_b = manager->create_object<Factory_B>();
		res &= UNIT_TEST_ERROR( "[Factory] Test create_object()", ( obj_b == nil ));
		Factory_A* obj_c = manager->create_object<Factory_C>();
		res &= UNIT_TEST_ERROR( "[Factory] Test create_object()", ( obj_c != nil ));
		Factory_A* obj_d = manager->create_object<Factory_D>();
		res &= UNIT_TEST_ERROR( "[Factory] Test create_object()", ( obj_d == nil ));
		MemoryManager::instance()->desallocate(obj_c);
	}}

	// Get number of bytes
	manager->clear();
	res &= UNIT_TEST_ERROR( "[Rtti] Test memory leak", ( bytes == MemoryManager::instance()->bytes() ));

	// Return result
	return res;
}


#ifdef GAMEENGINE_NAMESPACES
} // namespace GameEngine
#endif
